// (C) 2021 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

`timescale 1ns / 1ps

module uart_16550 #(                    
    parameter DEVICE_FAMILY = "MAX 10 FPGA",
    parameter ONE_CHAR_TIME = 14'd9548
)

(

// Avalon Memory Mapped Slave input
    input   wire                clk,                        // System clock
    input   wire                reset_n,                    // System Reset
    input   wire                channel_reset_n,            // reset & pltrst_n & !pc_channel_reset
    input   wire    [15:0]      avmm_addr,                  // AVMM address
    input   wire    [31:0]      avmm_writedata,             // AVMM Write data
    output  wire    [31:0]      avmm_readdata,              // AVMM Read data
    output  wire                avmm_readdatavalid,         // AVMM Read data valid
    input   wire                avmm_write,                 // Write enable
    input   wire                avmm_read,                  // Read enable
// Interrupts
    output  reg     [7:0]       vw_irq0,                    // Virtual Wire IRQ0 (eSPI Master)
    output  reg     [7:0]       vw_irq1,                    // Virtual Wire IRQ1 (eSPI Master)
    output  reg                 espi_serial_msix_req,       // MSI-X interrupt request (BMC FW)
    input   wire                espi_serial_msix_ack,       // MSI-X interrupt acknowledge (BMC FW)

// THR fifo is written by espi Master, read by BMC FW over AVMM 
// RBR fifo is written by BMC FW over AVMM, read by espi Master 
    input   wire                flush_vuart_fifo,
    input   wire                vuart_rbr_get,              // espi master reads fifo request
    output  wire [7:0]          vuart_rbr_rdata,            // espi master read fifo data
    output  wire                vuart_rxfifo_empty,
    input  wire                 iir_get_sts,                // Read of IIR register
    input  wire                 lsr_get_sts,                // Read of LSR register
    input  wire                 msr_get_sts,                // Read of MSR register

// UART registers for eSPI Master Access
// Write strobes from eSPI Master
    input  wire     [7:0]       espi_writedata,             // Write data from espi master
    input  wire                 flg_vuart_ier_w,            // Write strobe for IER register
    input  wire                 flg_vuart_dll_w,            // Write strobe for DLL register
    input  wire                 flg_vuart_dlh_w,            // Write strobe for DLM register
    input  wire                 flg_vuart_fcr_w,            // Write strobe for FCR register
    input  wire                 flg_vuart_lcr_w,            // Write strobe for LCR register
    input  wire                 flg_vuart_mcr_w,            // Write strobe for MCR register
    input  wire                 flg_vuart_scr_w,            // Write strobe for scratch register
    input  wire                 flg_vuart_thr_w,            // Write strobe for THR register

// UART Registers for eSPI Master readback
    output  reg     [7:0]       vuart_avl_dll,              // DLL register for espi master readback
    output  reg     [7:0]       vuart_avl_dlh,              // DLH register for espi master readback
    output  reg     [7:0]       vuart_avl_ier,              // IER register for espi master readback
    output  reg     [7:0]       vuart_avl_iir,              // IIR register for espi master readback
    output  reg     [7:0]       vuart_avl_lcr,              // LCR register for espi master readback
    output  reg     [7:0]       vuart_avl_mcr,              // MCR register for espi master readback
    output  reg     [7:0]       vuart_avl_lsr,              // LSR register for espi master readback
    output  reg     [7:0]       vuart_avl_msr,              // MSR register for espi master readback
    output  reg     [7:0]       vuart_avl_scr,              // SCR register for espi master readback
    output  reg    [15:0]       vuart_avl_addr              // ADDR register for espi address decode 
);

localparam      RBR_ADDR            =    5'd0,                // Receive Buffer Register (RO)
                THR_ADDR            =    5'd0,                // Transmit Holding Register (WO)
                DLL_ADDR            =    5'd0,                // Divisor Latch LSB Register (RW)
                IER_ADDR            =    5'd1,                // Interrupt Enable Register 
                DLM_ADDR            =    5'd1,                // Divisor Latch MSB Register (RW)
                IIR_ADDR            =    5'd2,                // Interrupt Identification Register (RO)
                FCR_ADDR            =    5'd2,                // FIFO Control Register (WO)
                LCR_ADDR            =    5'd3,                // Line Control Register (RW) Only bit 7 (DLAB)  used
                MCR_ADDR            =    5'd4,                // Modem Control Register (WO)
                LSR_ADDR            =    5'd5,                // Line Status Register (RO)
                MSR_ADDR            =    5'd6,                // Modem Status Register (RO)
                SCR_ADDR            =    5'd7,                // Scratch Register (RW)
                ADDR_ADDR           =    5'd8,                // Address offset (RW)
                FSR_ADDR            =    5'd9,                // FIFO Status offset (RO)
                ISR_ADDR            =    5'd10;               // Interrupt Status offset (RW)
                
localparam      RX_FIFO_CTL_IDLE      = 4'b0000,
                RX_FIFO_CTL_S1        = 4'b0001,
                RX_FIFO_CTL_S2        = 4'b0010,
                RX_FIFO_CTL_S3        = 4'b0100,
                RX_FIFO_CTL_S4        = 4'b1000;

localparam      TX_FIFO_CTL_IDLE      = 4'b0000,
                TX_FIFO_CTL_S1        = 4'b0001,
                TX_FIFO_CTL_S2        = 4'b0010,
                TX_FIFO_CTL_S3        = 4'b0100,
                TX_FIFO_CTL_S4        = 4'b1000;
                
localparam      TSR_IDLE              = 3'b000,
                TSR_S1                = 3'b001,
                TSR_S2                = 3'b010,
                TSR_S3                = 3'b100;

localparam      RX_TIMEOUT_IDLE      = 3'b000,
                RX_TIMEOUT_S1        = 3'b001,
                RX_TIMEOUT_S2        = 3'b010,
                RX_TIMEOUT_S3        = 3'b100;

localparam FOUR_CHAR_TIME = ONE_CHAR_TIME*4;
localparam NUM_WORDS_USED = 7;
localparam BAUD_DIVISOR   = ONE_CHAR_TIME / 11;         
            
// UART Registers
wire            dcd_fb;                 // Data Carrier Detect
wire            ri_fb;                  // Ring Indicator
wire            cts_fb;                 // Clear To Send
wire            dsr_fb;                 // Data Set Ready
assign dcd_fb = 1'b1;
assign ri_fb  = 1'b1;
assign cts_fb = 1'b1;
assign dsr_fb = 1'b1;
// Modem Control Register
wire            fifos_enabled;            // vuart_avl_fcr[0]
reg             fifos_enabled_dly;        // Used to detect change in fifos_enabled
reg     [1:0]   fifos_enabled_sync;       // Sync fifos_enabled to clk domain

// Modem Status Register
reg     [2:0]   ri_dly  = 3'h0;
reg     [1:0]   dcd_dly = 2'h0;
reg     [1:0]   cts_dly = 2'h0;
reg     [1:0]   dsr_dly = 2'h0;
wire            dcd;
wire            ri;
wire            cts;
wire            dsr;
reg             dcd_last_read = 1'b0;
reg             cts_last_read = 1'b0;
reg             dsr_last_read = 1'b0;
wire            dcd_delta;
wire            cts_delta;
wire            dsr_delta;
reg             ri_trail      = 1'b0; 
wire            ri_neg_edge;

reg     [15:0]  counter;
reg     [13:0]  tsr_count;
reg     [13:0]  rsr_count;

// Interrupt
wire            line_stat_int_en;
wire            rx_data_avail_int_en;
wire            rx_timeout_int_en;
wire            thr_empty_int_en;
reg             thr_empty_int_en_1d = 1'b0;
wire            modem_status_int_en;

reg             line_stat_int     = 1'b0;  // Priority 1 (highest)
reg             rx_data_avail_int = 1'b0;  // Priority 2
wire            rx_timeout_int;            // Priority 2
reg             thr_empty_int     = 1'b0;  // Priority 3
reg             modem_status_int  = 1'b0;  // Priority 4 (lowest)

reg     [31:0]  data_out_reg      = 32'h0;

// Line Status Register
wire            break_ind;
wire            framing_err;
wire            parity_err;
wire            rcv_data_rdy;
reg             rx_fifo_overrun;
reg     [2:0]   ext_rx_fifo_overrun_dly;    // Synchronize external FIFO overrun to pclk

// Local FIFO
reg     [1:0]   rx_fifo_clr_dly   = 2'h0;
wire            rx_fifo_clr;
reg     [3:0]   rx_fifo_trig_lvl;
wire            rx_err_flag;
wire            rx_fifo_full;
wire    [NUM_WORDS_USED:0]    rx_fifo_usedw;
reg     [7:0]   vuart_rbr_fifo_usedw_old;

reg     [1:0]   tx_fifo_clr_dly   = 2'h0;
wire            tx_fifo_clr;
wire            tx_fifo_rdreq;
wire            thr_empty;
reg             thr_empty_dly;
reg             avmm_read_dly   = 1'b0;
reg             avmm_write_dly   = 1'b0;
wire            uart_read_trailing_edge;
wire            tsr_busy;                    // Indicates Transmit State Machine busy
reg     [1:0]   tsr_busy_dly;

// Interrupt state machines
reg     [3:0]   tx_fifo_ctl_state;
reg     [3:0]   rx_fifo_ctl_state;
reg     [2:0]   rx_timeout_state;

reg             vuart_thr_put_din;
reg             vuart_thr_get_dout;
reg [7:0]       vuart_thr_din_wdata;
wire [7:0]      vuart_thr_rdata;
wire            vuart_txfifo_empty;
wire [7:0]      vuart_thr_fifo_usedw;
wire [7:0]      vuart_rbr_fifo_usedw;
wire            vuart_txfifo_rst;
wire            vuart_rxfifo_rst;
reg             vuart_rbr_put;
reg             vuart_avl_rbr_en;
reg             vuart_thr_rd_dly;
reg             vuart_avl_thr_en;
reg             vuart_rbr_wr_dly;
reg  [7:0]      vuart_rbr_put_data;
reg  [31:0]     vuart_avl_isr = 32'h0;
reg  [31:0]     vuart_avl_isr_clr = 32'h0;
wire [31:0]     vuart_avl_fsr;
reg  [7:0]      vuart_avl_fcr;
reg  [7:0]      avmm_lcr;
reg  [7:0]      vuart_rbr_wdata;
reg             thr_bmc_ready = 1'b0;
reg  [7:0]      tx_char_count = 8'b1;
reg             iir_get_sts_1d = 1'b0;
reg             lsr_get_sts_1d = 1'b0;
reg             msr_get_sts_1d = 1'b0;
wire            avmm_dlab;
wire            espi_dlab;

wire ext_rx_fifo_empty;
wire pirq;
wire ext_rx_fifo_overrun;
wire vuart_rxfifo_full;

//**********************
// UART Register Write (eSPI Master writes espi_dlab, dll, dlh, ier, fcr, lcr, mcr, scr)
//**********************
always @ ( posedge clk )
begin
    if ( !channel_reset_n )
    begin
        vuart_avl_dll       <= 8'h6d;
        vuart_avl_dlh       <= 8'b0;
        vuart_avl_ier       <= 8'b0;
        vuart_avl_fcr       <= 8'b11000001;
        vuart_avl_lcr       <= 8'h3;
        vuart_avl_mcr       <= 8'b0;
        vuart_avl_scr       <= 8'b0;
    end
    else
    begin
      if ( flg_vuart_dll_w )
        vuart_avl_dll     <= espi_writedata[7:0];
      if ( flg_vuart_dlh_w )
        vuart_avl_dlh     <= espi_writedata[7:0];
      if ( flg_vuart_ier_w )
        vuart_avl_ier     <= {4'h0, espi_writedata[3:0]};
      if ( flg_vuart_fcr_w )
        vuart_avl_fcr     <= {espi_writedata[7:3], 2'b00, espi_writedata[0]};
      if ( flg_vuart_lcr_w )
        vuart_avl_lcr     <= espi_writedata[7:0];
      if ( flg_vuart_mcr_w )
        vuart_avl_mcr     <= {6'h0, espi_writedata[1:0]};
      if ( flg_vuart_scr_w )
        vuart_avl_scr     <= espi_writedata[7:0];
    end    
end

//**********************
// UART Register Write (AVMM Writes THR, avmm DLAB, ADDR and ISR)
//**********************
always @ ( posedge clk )
begin
    if ( !reset_n )
    begin
        avmm_lcr          <= 8'b0;
        vuart_avl_addr    <= 16'b0;
        vuart_rbr_wdata   <= 8'b0;
        vuart_avl_isr_clr <= 32'b0;
    end
    else
    if ( avmm_write )
    begin
        case ( avmm_addr[4:0] )
            THR_ADDR:  if (~avmm_dlab) vuart_rbr_wdata   <= avmm_writedata[7:0];
            LCR_ADDR:                  avmm_lcr          <= avmm_writedata[7:0];
            ADDR_ADDR:                 vuart_avl_addr    <= avmm_writedata[15:0];
            ISR_ADDR:                  vuart_avl_isr_clr <= 32'h0000_0001;
        endcase
    end    
    else // clear isr
    begin
            vuart_avl_isr_clr  <= 32'h0;
    end    
end
// pipeline to match fifo latency
reg [3:0] rd_sr = 4'b0;
wire avmm_read_dly2;

always @(posedge clk )
 rd_sr <= {rd_sr[2:0], avmm_read};

assign avmm_read_dly2 = rd_sr[2];
// Latch Address
reg [4:0] address_reg = 5'h0;
always @(posedge clk)
begin
    if (~reset_n)
         address_reg <= 5'b0;
    else if (avmm_read)
         address_reg <= avmm_addr[4:0];
end


assign avmm_readdatavalid = rd_sr[3];

//**********************
// UART Register Read over AVMM
//**********************
always @( posedge clk )
begin
    if (~reset_n)
      data_out_reg <= 32'd0;
    else if ( avmm_read_dly2 )
    begin
        case ( address_reg[4:0] )
            RBR_ADDR:                                    // Offset 0
                if ( avmm_dlab == 1'b1 )
                    data_out_reg    <= {24'h0, vuart_avl_dll};
                else
                    data_out_reg    <= {24'h0, vuart_thr_rdata};// RBR register i/f, reading THR from espi
            
            IER_ADDR:                                    // Offset 1
                if ( avmm_dlab == 1'b1 )
                    data_out_reg    <= {24'h0, vuart_avl_dlh};
                else
                    data_out_reg    <= {24'h0, vuart_avl_ier};
                    
            IIR_ADDR:                                    // Offset 2
                data_out_reg        <= {24'h0, vuart_avl_iir};
                
            LCR_ADDR:                                    // Offset 3
                data_out_reg        <= {24'h0, vuart_avl_lcr};
                
            MCR_ADDR:                                    // Offset 4
                data_out_reg        <= {24'h0, vuart_avl_mcr};
                
            LSR_ADDR:                                    // Offset 5
                data_out_reg        <= {24'h0, vuart_avl_lsr};
                
            MSR_ADDR:                                    // Offset 6
                data_out_reg        <= {24'h0, vuart_avl_msr};
                
            SCR_ADDR:                                    // Offset 7
                data_out_reg        <= {24'h0, vuart_avl_scr};
                
            ADDR_ADDR:                                   // Offset 8
                data_out_reg        <= {16'h0, vuart_avl_addr};
                
            FSR_ADDR:                                    // Offset 9
                data_out_reg        <= vuart_avl_fsr;
                
            ISR_ADDR:                                    // Offset 10
                data_out_reg        <= vuart_avl_isr;
                
            default:
                data_out_reg        <= 8'h00;
        endcase
    end
end

assign vuart_avl_fsr      = {8'd0, vuart_thr_fifo_usedw, 8'd0, vuart_rbr_fifo_usedw};
assign avmm_readdata      = data_out_reg;

// Controls the transfer from the internal FIFO into the TSR
always @ ( posedge clk )                
begin
    if ( !channel_reset_n )
    begin
        tx_fifo_ctl_state       <= TX_FIFO_CTL_IDLE;
        tsr_count               <= 14'b0;
    end
    else if ( vuart_txfifo_empty )
        tx_fifo_ctl_state       <= TX_FIFO_CTL_IDLE;
    else
    begin
        case ( tx_fifo_ctl_state )
            TX_FIFO_CTL_IDLE:
            begin
                tsr_count               <= 14'b0;
                
                if ( thr_bmc_ready )
                    tx_fifo_ctl_state       <= TX_FIFO_CTL_IDLE;
                else if ( !vuart_txfifo_empty )
                    tx_fifo_ctl_state       <= TX_FIFO_CTL_S1;
                else
                    tx_fifo_ctl_state       <= TX_FIFO_CTL_IDLE;
            end
                
            TX_FIFO_CTL_S1:
            begin
                tsr_count               <= tsr_count + 1'b1;
                
                if ( tsr_count == ONE_CHAR_TIME )
                    tx_fifo_ctl_state       <= TX_FIFO_CTL_S2;
                else
                    tx_fifo_ctl_state       <= TX_FIFO_CTL_S1;
            end
   
            TX_FIFO_CTL_S2:
            begin
                tx_fifo_ctl_state       <= TX_FIFO_CTL_S3;
            end
   
            TX_FIFO_CTL_S3:
                tx_fifo_ctl_state       <= TX_FIFO_CTL_IDLE;

        endcase
    end
end

// Generate TX FIFO read and Ext. RX FIFO write pulses
assign tx_fifo_rdreq =          ( tx_fifo_ctl_state == TX_FIFO_CTL_S3 );

// Count the number of TX character times
always @ ( posedge clk )                
  if ( !channel_reset_n )
    tx_char_count <= 8'b1;
  else if ( vuart_txfifo_empty )
    tx_char_count <= 8'b1;
  else if ( tx_fifo_rdreq )
    tx_char_count <= tx_char_count + 1'b1;

// Mark THR FIFO ready if there is data in the fifo and we have waited that many character times
always @ ( posedge clk )                
  if ( !channel_reset_n )
    thr_bmc_ready <= 1'b0;
  else if ( vuart_txfifo_empty )
    thr_bmc_ready <= 1'b0;
  else if ( tx_fifo_rdreq & (tx_char_count == vuart_thr_fifo_usedw[7:0]))
    thr_bmc_ready <= 1'b1;

assign tsr_busy =               ( tx_fifo_ctl_state != TX_FIFO_CTL_IDLE );

assign ext_rx_fifo_empty = 1'b0;

always @(posedge clk)
  if (~channel_reset_n) 
    espi_serial_msix_req  <= 1'b0;
  else if (~vuart_avl_isr[0] & thr_bmc_ready)
    espi_serial_msix_req  <= 1'b1;
  else if (espi_serial_msix_ack == 1'b1)
    espi_serial_msix_req  <= 1'b0;


// Condition to assert an interrupt to espi master
always @(posedge clk)
  if (!channel_reset_n)
  begin
    vw_irq0 <= 8'd0;
    vw_irq1 <= 8'd0;
  end
  else if (vuart_avl_iir[0] == 1'b0)
    vw_irq0 <= {1'b1, 7'd4};
  else
    vw_irq0 <= {1'b0, 7'd4};

always @(posedge clk)
  if (~channel_reset_n) 
  begin
    vuart_avl_isr[31:00]  <= 32'b0;
  end 
  else if (vuart_avl_isr[0] == 1'b1) 
    begin
      if (vuart_avl_isr_clr[0] == 1'b1 )
        vuart_avl_isr[0]  <= 1'b0;
    end
  else if (thr_bmc_ready)
    begin
      vuart_avl_isr[0]  <= 1'b1;
    end


// This state machine simulates clocking a character through the RSR and into the internal RX FIFO
always @ ( posedge clk )                
begin
    if ( !channel_reset_n )
    begin
        fifos_enabled_sync        <= 2'b00;
        rx_fifo_ctl_state         <= RX_FIFO_CTL_IDLE;
        rsr_count                 <= 14'b0;
    end
    else
    begin
        case ( rx_fifo_ctl_state )
            RX_FIFO_CTL_IDLE:
            begin
                rsr_count               <= 14'b0;
                
                if ( !ext_rx_fifo_empty && ( fifos_enabled_sync[1] ? !rx_fifo_full : vuart_rxfifo_empty ) )
                    rx_fifo_ctl_state       <= RX_FIFO_CTL_S1;
                else
                    rx_fifo_ctl_state       <= RX_FIFO_CTL_IDLE;
            end
        
            RX_FIFO_CTL_S1:
                rx_fifo_ctl_state       <= RX_FIFO_CTL_S2;

            RX_FIFO_CTL_S2:
            begin
                rsr_count               <= rsr_count + 1'b1;
                
                if ( rsr_count == ONE_CHAR_TIME )
                    rx_fifo_ctl_state       <= RX_FIFO_CTL_S3;
                else
                    rx_fifo_ctl_state       <= RX_FIFO_CTL_S2;
            end
        
            RX_FIFO_CTL_S3:
                rx_fifo_ctl_state       <= RX_FIFO_CTL_IDLE;
            
        endcase
        
        // Synchronize FIFOs Enabled
        fifos_enabled_sync[1]       <= fifos_enabled_sync[0];
        fifos_enabled_sync[0]       <= fifos_enabled;
    end
end

//**********************
// Line Control Register
//**********************
assign espi_dlab =   vuart_avl_lcr[7];  // espi controls DLAB
assign avmm_dlab =   avmm_lcr[7];       // bmc fw controls DLAB

//**********************
// FIFO Control Register
//**********************
always @ ( posedge clk )
begin
    if ( !channel_reset_n )
    begin
        tx_fifo_clr_dly         <= 2'b0;
        rx_fifo_clr_dly         <= 2'b0;
        fifos_enabled_dly       <= 1'b0;
    end
    
    // Generate FIFO clear pulses based on FCR bit writes
    else
    begin
        tx_fifo_clr_dly[1]      <= tx_fifo_clr_dly[0];
        tx_fifo_clr_dly[0]      <= vuart_avl_fcr[1];

        rx_fifo_clr_dly[1]      <= rx_fifo_clr_dly[0];
        rx_fifo_clr_dly[0]      <= vuart_avl_fcr[2];
        
        fifos_enabled_dly       <= fifos_enabled;
    end
end

assign     fifos_enabled =         vuart_avl_fcr[0];

//fcr
// bit 1 resets the txfifo
assign vuart_txfifo_rst = tx_fifo_clr;
// bit 2 resets the rxfifo
assign vuart_rxfifo_rst = rx_fifo_clr;

assign tx_fifo_clr      = ( tx_fifo_clr_dly[0] && !tx_fifo_clr_dly[1] ) || ( fifos_enabled != fifos_enabled_dly );
assign rx_fifo_clr      = ( rx_fifo_clr_dly[0] && !rx_fifo_clr_dly[1] ) || ( fifos_enabled != fifos_enabled_dly );

// FIFO trigger level logic
always @ ( vuart_avl_fcr[7:6] )
begin
    case ( vuart_avl_fcr[7:6] )
        2'b00 : rx_fifo_trig_lvl    = 1;
        2'b01 : rx_fifo_trig_lvl    = 4;
        2'b10 : rx_fifo_trig_lvl    = 8;
        2'b11 : rx_fifo_trig_lvl    = 14;
    endcase    
end
        
//**********************
// Interrupt Enable Register
//**********************
assign rx_data_avail_int_en =  vuart_avl_ier[0];
assign rx_timeout_int_en    =  vuart_avl_ier[0];
assign thr_empty_int_en     =  vuart_avl_ier[1];
assign line_stat_int_en     =  vuart_avl_ier[2];
assign modem_status_int_en  =  vuart_avl_ier[3];

//**********************
// Interrupt Sources
//**********************
// Line Status Interrupt - first (highest) priority
always @ ( posedge clk )
begin
    if ( !channel_reset_n )
        line_stat_int           <= 1'b0;
    
    // If Interrupt Enabled...
    else if ( line_stat_int_en )
    begin
        // Cleared on Line Status Register read
        //if    ( flg_vuart_lsr_r )
        if    ( ~lsr_get_sts & lsr_get_sts_1d )
            line_stat_int           <= 1'b0;    
            
        // Set on FIFO overrun
        else if ( ( ext_rx_fifo_overrun_dly[1] && !ext_rx_fifo_overrun_dly[2] ) )
            line_stat_int           <= 1'b1;
    end
    // Else...(Interrupt NOT Enabled)
    else
        line_stat_int            <= 1'b0;
end
    
// Received Data Available
always @ ( posedge clk )
begin
    if ( !channel_reset_n )
        rx_data_avail_int       <= 1'b0;
    
    // If Interrupt Enabled...            
    else if ( rx_data_avail_int_en )
    begin
        if ( fifos_enabled && ( vuart_rbr_fifo_usedw < rx_fifo_trig_lvl ) )
            rx_data_avail_int       <= 1'b0;    
            
        else if ( fifos_enabled && ( vuart_rbr_fifo_usedw >= rx_fifo_trig_lvl )) 
            rx_data_avail_int       <= 1'b1;

    end
    
    // Else...(Interrupt NOT Enabled)
    else
        rx_data_avail_int       <= 1'b0;
end

// Reception Timeout - second priority
always @ (posedge clk )
begin
    if ( !channel_reset_n )
    begin
        rx_timeout_state        <= RX_TIMEOUT_IDLE;
    end
    
    // If FIFOs and Interrupt Enabled...    
    else if ( fifos_enabled && rx_timeout_int_en )
    begin
        case ( rx_timeout_state )
        
            RX_TIMEOUT_IDLE:
            begin
                if ( !vuart_rxfifo_empty )
                    rx_timeout_state        <= RX_TIMEOUT_S1;
                else
                    rx_timeout_state        <= RX_TIMEOUT_IDLE;
            end
                
            RX_TIMEOUT_S1:
            begin    
                counter <= 16'b0;
                
                if ( vuart_rxfifo_empty )
                    rx_timeout_state        <= RX_TIMEOUT_IDLE;
                else 
                begin
                    if ( vuart_rbr_fifo_usedw_old[NUM_WORDS_USED:0] != vuart_rbr_fifo_usedw[NUM_WORDS_USED:0] )

                        rx_timeout_state        <= RX_TIMEOUT_S1;
                    else
                        rx_timeout_state        <= RX_TIMEOUT_S2;
                end
            end

            RX_TIMEOUT_S2:
            begin
                counter <= counter + 1'b1;
                
                if  ( vuart_rbr_fifo_usedw_old[NUM_WORDS_USED:0] != vuart_rbr_fifo_usedw[NUM_WORDS_USED:0] )      // Reset counter on either data read or arriving data
                    rx_timeout_state        <= RX_TIMEOUT_S1;
                else
                if ( counter == FOUR_CHAR_TIME )
                    rx_timeout_state        <= RX_TIMEOUT_S3;                // Interrupt set on no characters added or removed during last 4 Character Time
                else
                    rx_timeout_state        <= RX_TIMEOUT_S2;
            end
         
            RX_TIMEOUT_S3:
            begin
                if ( vuart_rxfifo_empty )
                    rx_timeout_state        <= RX_TIMEOUT_IDLE;
                else
                if ( vuart_rbr_get)     // Interrupt cleared on RBR read - espi reading data
                    rx_timeout_state        <= RX_TIMEOUT_S1;
                else
                    rx_timeout_state        <= RX_TIMEOUT_S3;
            end
            
        endcase
        
        vuart_rbr_fifo_usedw_old[NUM_WORDS_USED:0]        <= vuart_rbr_fifo_usedw[NUM_WORDS_USED:0];        // Store previous word count to determine if characters have been added or removed from RBR    
    end
    
    // Else...(Interrupt or FIFOs NOT Enabled)
    else
    begin
        rx_timeout_state            <= RX_TIMEOUT_IDLE;                    // Default, if FIFOs not enabled or interrupt not enabled
    end
end

// Timeout Interrupt Assignment
assign rx_timeout_int =         ( rx_timeout_state == RX_TIMEOUT_S3 ) && rx_timeout_int_en;

// Data direction: from espi master to PCIe
always @ ( posedge clk )
begin
    if ( !channel_reset_n )
        thr_empty_int           <= 1'b0;
        
    // If Interrupt Enabled...        
    else if ( thr_empty_int_en )
    begin
        if    ( vuart_thr_put_din  || (~iir_get_sts & iir_get_sts_1d))
            thr_empty_int           <= 1'b0;
        else if ( thr_empty && !thr_empty_dly )
            thr_empty_int           <= 1'b1;
        else if ( thr_empty && !thr_empty_int_en_1d )
            thr_empty_int           <= 1'b1;
    end
    
    // Else...(Interrupt NOT Enabled)
    else
        thr_empty_int            <= 1'b0;
end
    
// Modem Status - Lowest (fourth) priority
always @ ( posedge clk )
begin
    if ( !channel_reset_n )
        modem_status_int        <= 1'b0;
        
    // If Interrupt Enabled...    
    else if ( modem_status_int_en )
    begin
        // Clear on MSR read
        if ( ~msr_get_sts & msr_get_sts_1d )
            modem_status_int        <= 1'b0;
            
        // Set on CTS or DSR or RI or DCD
        else if ( ( dcd_delta || cts_delta || dsr_delta || ri_neg_edge ) )
            modem_status_int        <= 1'b1;            
    end
    
    // Else...(Interrupt NOT Enabled)
    else
        modem_status_int        <= 1'b0;    
end

//**********************
// Interrupt ID Register
//**********************
always @ ( posedge clk )
begin
    if ( !channel_reset_n )
        vuart_avl_iir                     <= 8'b11000001;
    else
    begin
        vuart_avl_iir[5:4]                <= 2'b00;
        
        if ( line_stat_int )                                  // Highest priority
            vuart_avl_iir[3:0]            <= 4'b0110;
            
        else
        if ( rx_data_avail_int || rx_timeout_int )            // Second Priority
        begin
            vuart_avl_iir[3]              <= rx_timeout_int;
            vuart_avl_iir[2:0]            <= 3'b100;
        end
        
        else
        if ( thr_empty_int )                                  // Third Priority
            vuart_avl_iir[3:0]            <= 4'b0010;
            
        else
        if ( modem_status_int )                               // Fourth Priority
            vuart_avl_iir[3:0]            <= 4'b0000;
            
        else
            vuart_avl_iir[3:0]            <= 4'b0001;
            
        if ( fifos_enabled )                                  // IIR bits 7,6 set if FIFOs enabled
            vuart_avl_iir[7:6]            <= 2'b11;           // Indicates 16550 mode
        else
            vuart_avl_iir[7:6]            <= 2'b00;           // Otherwise indicate 16450 mode
    end
end

// IRQ
assign pirq                = !vuart_avl_iir[0];
assign ext_rx_fifo_overrun = 1'b0;

//**********************
// Line Status Register
//**********************
always @ ( posedge clk )        
begin
    if ( !channel_reset_n )
    begin
        ext_rx_fifo_overrun_dly[2:0]    <= 3'b000;
        rx_fifo_overrun                 <= 1'b0;
        tsr_busy_dly[1:0]               <= 2'b00;
        thr_empty_dly                   <= 1'b0;        
    end
    else
    begin
        // Set on detection of overrun condition
        if ( ext_rx_fifo_overrun_dly[1] && !ext_rx_fifo_overrun_dly[2] )
            rx_fifo_overrun             <= 1'b1;
            
        // Reset on read of LSR    
        else if    ( ~lsr_get_sts & lsr_get_sts_1d )
        //else if ( flg_vuart_lsr_r )
            rx_fifo_overrun             <= 1'b0;
    
        // Synchronize external FIFO overrun to clk
        ext_rx_fifo_overrun_dly[2]      <= ext_rx_fifo_overrun_dly[1];
        ext_rx_fifo_overrun_dly[1]      <= ext_rx_fifo_overrun_dly[0];
        ext_rx_fifo_overrun_dly[0]      <= ext_rx_fifo_overrun;
        
        // Synchronize TSR Busy (i.e. not empty)
        tsr_busy_dly[1]                 <= tsr_busy_dly[0];    
        tsr_busy_dly[0]                 <= tsr_busy;            
        
        // Synchronize THR Empty
        thr_empty_dly                   <= thr_empty;
    end
end

assign rcv_data_rdy =  !vuart_rxfifo_empty;
assign parity_err =    1'b0;
assign framing_err =   1'b0;
assign break_ind =     1'b0;
assign rx_err_flag =   fifos_enabled && ( parity_err || framing_err || break_ind || rx_fifo_overrun );
assign thr_empty =     vuart_txfifo_empty;

always @ ( posedge clk )
begin
    vuart_avl_lsr[7]  <= rx_err_flag;
    vuart_avl_lsr[6]  <= thr_empty && !tsr_busy_dly[1];
    vuart_avl_lsr[5]  <= thr_empty;
    vuart_avl_lsr[4]  <= break_ind;
    vuart_avl_lsr[3]  <= framing_err;
    vuart_avl_lsr[2]  <= parity_err;
    vuart_avl_lsr[1]  <= rx_fifo_overrun;
    vuart_avl_lsr[0]  <= rcv_data_rdy;
end

//**********************
// Modem Status Register
//**********************
// Synchronize modem status signals from clk to clk domains    
always @ ( posedge clk )
begin
    if ( !channel_reset_n )
    begin
        dcd_dly[1:0]        <= 2'b11;
        ri_dly[1:0]         <= 2'b11;
        cts_dly[1:0]        <= 2'b11;
        dsr_dly[1:0]        <= 2'b11;
    end
    else
    begin
        dcd_dly[1]          <= dcd_dly[0];
        dcd_dly[0]          <= dcd_fb;

        ri_dly[2]           <= ri_dly[1];
        ri_dly[1]           <= ri_dly[0];        
        ri_dly[0]           <= ri_fb;

        cts_dly[1]          <= cts_dly[0];        
        cts_dly[0]          <= cts_fb;

        dsr_dly[1]          <= dsr_dly[0];
        dsr_dly[0]          <= dsr_fb;
    end
end

assign dcd =                dcd_dly[1];
assign ri  =                ri_dly[1];
assign cts =                cts_dly[1];
assign dsr =                dsr_dly[1];

assign dcd_delta =          ( dcd_last_read == dcd ) ? 1'b0 : 1'b1;
assign cts_delta =          ( cts_last_read == cts ) ? 1'b0 : 1'b1;
assign dsr_delta =          ( dsr_last_read == dsr ) ? 1'b0 : 1'b1;
assign ri_neg_edge =        ( !ri && ri_dly[2] );

always @ ( posedge clk )
begin
    if ( !channel_reset_n )
        ri_trail            <= 1'b0;
    else
    if ( ri_neg_edge )
        ri_trail            <= 1'b1;
    else
    if ( ~msr_get_sts & msr_get_sts_1d )
        ri_trail            <= 1'b0;
end

always @ ( posedge clk )
begin
    if ( !reset_n )
    begin
        avmm_read_dly       <= 1'b0;
        avmm_write_dly      <= 1'b0;
        iir_get_sts_1d      <= 1'b0;
        lsr_get_sts_1d      <= 1'b0;
        msr_get_sts_1d      <= 1'b0;
        thr_empty_int_en_1d <= 1'b0;
    end
    else
    begin
        avmm_read_dly       <= avmm_read;
        avmm_write_dly      <= avmm_write;
        iir_get_sts_1d      <= iir_get_sts;
        lsr_get_sts_1d      <= lsr_get_sts;
        msr_get_sts_1d      <= msr_get_sts;
        thr_empty_int_en_1d <= thr_empty_int_en;
    end
end

assign uart_read_trailing_edge =    ( !avmm_read && avmm_read_dly );

always @ ( posedge clk )
begin
    if ( !channel_reset_n )
    begin
        dcd_last_read       <= 1'b1;
        cts_last_read       <= 1'b1;
        dsr_last_read       <= 1'b1;
    end
    else if ( ~msr_get_sts & msr_get_sts_1d )
    begin
        dcd_last_read       <= dcd;
        cts_last_read       <= cts;
        dsr_last_read       <= dsr;
    end
end

always @ ( posedge clk )
begin
    vuart_avl_msr[7]    <= dcd;
    vuart_avl_msr[6]    <= ri;
    vuart_avl_msr[5]    <= dsr;
    vuart_avl_msr[4]    <= cts;
    vuart_avl_msr[3]    <= dcd_delta;
    vuart_avl_msr[2]    <= ri_trail;
    vuart_avl_msr[1]    <= dsr_delta;
    vuart_avl_msr[0]    <= cts_delta;
end

// FIFO instantiation and logic
always @(posedge clk)
begin
     if (avmm_read && (avmm_addr==RBR_ADDR) && (espi_dlab==1'b0) )
         vuart_avl_rbr_en <= 1'b1;
     else
         vuart_avl_rbr_en <= 1'b0;
end

// Written by espi master, read by BMC FW over AVMM
// Transmitter Holding Register - espi
always @(posedge clk)
begin
    vuart_thr_put_din   <= flg_vuart_thr_w;
    vuart_thr_din_wdata <= flg_vuart_thr_w ? espi_writedata:8'd0;

    vuart_thr_rd_dly    <= vuart_avl_rbr_en;
    vuart_thr_get_dout  <= {vuart_thr_rd_dly,vuart_avl_rbr_en}==2'b01;
end

espi_fifo #(

    .DEPTH  (256),
    .WIDTHU (8),
    .FAMILY (DEVICE_FAMILY)
) vuart_thr_inst (
    .clk          (clk),
    .rst_n        (channel_reset_n && (~flush_vuart_fifo) && (~vuart_txfifo_rst)),
    .put          (vuart_thr_put_din),
    .get          (vuart_thr_get_dout),
    .wdata        (vuart_thr_din_wdata),
    .full         (),
    .empty        (vuart_txfifo_empty),
    .rdata        (vuart_thr_rdata),
    .usedw        (vuart_thr_fifo_usedw)
);

// FIFO instantiation and logic
//  FIFO written by BMC FW via AVMM, read by espi master over espi
//  Fifo instance is RBR fifo
//  signals refer to rxfifo
//  AVMM write to this fifo via THR register, espi reads via vuart_rbr_rdata
//
always @(posedge clk)
begin
      if (avmm_write && (avmm_addr==THR_ADDR) && (avmm_dlab==1'b0) )
          vuart_avl_thr_en <= 1'b1;
      else
          vuart_avl_thr_en <= 1'b0;
end

// Receive Buffer Register - espi
always @(posedge clk)
begin
    // vuart_avl_thr_en asserts when avmm i/f writes THR, mapped to RBR
    vuart_rbr_wr_dly   <= vuart_avl_thr_en;
    vuart_rbr_put      <= {vuart_rbr_wr_dly,vuart_avl_thr_en}==2'b10;
    vuart_rbr_put_data <= vuart_rbr_wdata; // from avmm i/f THR register
end

espi_fifo #(
    .DSIZE  (8),
    .DEPTH  (256),
    .WIDTHU (8),
    .FAMILY (DEVICE_FAMILY)
) vuart_rbr_inst (
    .clk          (clk),
    .rst_n        (channel_reset_n && (~flush_vuart_fifo) && (~vuart_rxfifo_rst)),
    .put          (vuart_rbr_put),
    .get          (vuart_rbr_get),
    .wdata        (vuart_rbr_put_data),
    .full         (vuart_rxfifo_full),
    .empty        (vuart_rxfifo_empty),
    .rdata        (vuart_rbr_rdata),
    .usedw        (vuart_rbr_fifo_usedw)
);



endmodule
